package devices;

public class NetDevice implements java.lang.Runnable, core.interfaces.DeviceInterface, core.interfaces.MemoryCallbackInterface{

	private int baseAddress = 0xFFD0;
	private int interruptLine = 2;
	private int localPort = 65000;
	private int remotePort = 65001;
	private java.lang.String remoteIp = "localhost";

	private boolean running = false;

	private java.lang.Object lock = new java.lang.Object();
	private java.lang.Object lock2 = new java.lang.Object();
	private java.lang.Thread thread = null;

	private core.MemoryClass mem = null;
	private core.interfaces.InterruptInterface iInterface = null;
	private core.interfaces.MemoryCallbackInterface cal = null;

	public NetDevice(core.MemoryClass mem, core.interfaces.MemoryCallbackInterface cal, core.interfaces.InterruptInterface iObject, int baseAddr, int interruptLine){
		this.init(mem, cal, iObject, baseAddr, interruptLine);
	}

	public void init(core.MemoryClass mem, core.interfaces.MemoryCallbackInterface cal, core.interfaces.InterruptInterface iObject, int baseAddr, int interruptLine){
		this.setBaseAddress(baseAddr);
		this.setInterruptLine(interruptLine);
		this.mem = mem;
		this.iInterface = iObject;
		this.cal = cal;
		this.thread = new java.lang.Thread(this);
		this.thread.start();
	}

	public void run() {
		java.net.DatagramSocket socket = null;
		java.net.DatagramPacket packet = null;
		byte[] buffer = new byte[10];
		short[] buffer2 = new short[2];
		while(true){
			synchronized(lock){
				while(!this.running){
					try {
						this.lock.wait();
					} catch (InterruptedException e) {}
				}
			}
			try {
				socket = new java.net.DatagramSocket(this.localPort);
				socket.setSoTimeout(500);
				packet = new java.net.DatagramPacket(buffer,buffer.length);
				while(this.running){
					try{
						try{
							socket.receive(packet);
							util.Messages.info("Packet received\n");
							buffer = packet.getData();
							util.Messages.info("Buffer Length: " + buffer.length + "\n");
							if(buffer.length > 4){
								buffer2[0] = (short)((buffer[0]<<8) | (0xFF & buffer[1]));
								buffer2[1] = (short)((buffer[2]<<8) | (0xFF & buffer[3]));
								util.Messages.info(java.lang.String.format("Received Data: 0x%X, 0x%04X\n", buffer2[0], buffer2[1]));
								if(buffer2[0] == (short)0xFFFF){
									this.iInterface.raiseInterruptLine(this.interruptLine);
								}else if(((buffer2[0] & 0xFFF0) == 0) && ((buffer2[0] & 0xF) != 0)){
									int auxPos = ((buffer2[0] & 0x000F) | this.baseAddress);
									this.mem.ssetWord(auxPos, buffer2[1]);
									this.cal.memoryChanged();
								}else{
									util.Messages.error(java.lang.String.format("NetDevice: Unreadable data received: 0x%04X 0x%04X\n", buffer2[0], buffer2[1]));
								}
							}
						}catch(java.net.SocketTimeoutException e){}
					}catch(java.io.IOException e){
						util.Messages.error("NetDevice: I/O exception: " + e.getLocalizedMessage());
					}
				}
				synchronized(lock2){
					this.lock2.notifyAll();
				}
				socket.close();
			} catch (java.net.SocketException e) {
				this.running = false;
				util.Messages.error("NetDevice: Socket exception: " + e.getLocalizedMessage());
			}
		}
	}

	public void start(){
		if(this.running == false){
			this.running = true;
			synchronized(lock){
				this.lock.notifyAll();
			}
			util.Messages.info("Net Device Activation\n");
		}
	}
	public void stop(){
		this.running = false;
		util.Messages.info("Net Device DeActivation\n");
	}
	public void sstop(){
		if(this.running == true){
			synchronized(lock2){
				this.running = false;
				try {
					this.lock2.wait();
				} catch (InterruptedException e) {}
			}
			util.Messages.info("Net Device DeActivation\n");
		}
	}
	public boolean isRunning(){
		return(this.running);
	}

	public void configure(int localPort, java.lang.String remoteIp, int remotePort){
		this.localPort = localPort;
		this.remoteIp = remoteIp;
		this.remotePort = remotePort;
		util.Messages.info("NetDevice: new configuration: " + this.localPort + ", " + this.remoteIp + ":" + this.remotePort + "\n");
		if(this.running){
			this.sstop();
			this.start();
		}
	}
	public int getLocalPort(){
		return(this.localPort);
	}
	public int getRemotePort(){
		return(this.remotePort);
	}
	public java.lang.String getRemoteIp(){
		return(this.remoteIp);
	}
	public void setBaseAddress(int baseAddr) {
		this.baseAddress = baseAddr;
	}
	public void setInterruptLine(int line) {
		this.interruptLine = line;
	}
	public int getBaseAddress() {
		return(this.baseAddress);
	}
	public int getInterruptLine() {
		return(this.interruptLine);
	}
	public int getMemSize() {
		return(16);
	}

	public void memoryChanged() {
	}
	private void sendMessage(short c1, short c2){
		byte[] buff2 = new byte[4];
		java.net.InetAddress ia = null; //L'adreça del server
		java.net.DatagramSocket so = null;
		java.net.DatagramPacket dp_enviar = null;
		buff2[0] = (byte)((c1>>8) & 0xFF);
		buff2[1] = (byte)(c1 & 0xFF);
		buff2[2] = (byte)((c2>>8) & 0xFF);
		buff2[3] = (byte)(c2 & 0xFF);
		try{
			ia = java.net.InetAddress.getByName(this.remoteIp);
			try{
				dp_enviar = new java.net.DatagramPacket(buff2, buff2.length, ia, this.remotePort);
				so = new java.net.DatagramSocket();
				so.send(dp_enviar);
				util.Messages.info("Packet sent\n");
			}catch (java.io.IOException e) {
				util.Messages.error("I/O error:" + e.getLocalizedMessage());
			}
		} catch (java.net.UnknownHostException e) {
			util.Messages.error("Unknown Host: " + e.getLocalizedMessage());
		}
	}

	public void memoryChanged(int address) {
		if(this.running){
			if(this.baseAddress == (address & 0x0000FFF0)){
				util.Messages.info(java.lang.String.format("NetDevice: Address 0x%04X changed\n", address));
				short[] buff = new short[2];
				buff[1] = this.mem.getWord(address);
				if((address & 0xF) == 0){
					//Control address
					if((buff[1] & 0x1) == 1){
						buff[0] = (short)0xFFFF;
						this.sendMessage(buff[0], buff[1]);
					}else{
						buff[0] = (short)0;
					}
				}else{
					buff[0] = (short)(address & 0x0F);
					this.sendMessage(buff[0], buff[1]);
				}
			}
		}
	}
	public void memoryAccessed(int address) {
	}
}
